/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.util.nastranHdf5Parser;

import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.isfd.domain.IsfdMeshOp2Object;
import com.comac.ins.isfd.domain.IsfdMeshOp2Result;
import com.comac.ins.isfd.domain.bo.IsfdMeshOp2SubcaseBo;
import com.comac.ins.isfd.util.nastranHdf5Parser.constant.NastranDatasetConfig;
import com.comac.ins.isfd.util.nastranHdf5Parser.entity.NastranData;
import io.jhdf.HdfFile;
import io.jhdf.api.Attribute;
import io.jhdf.api.Dataset;
import io.jhdf.api.Group;
import io.jhdf.api.Node;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class NastranHdf5Reader {

    private HdfFile file;
    private NastranData data;

    public void open(String h5Filename) throws IOException {
        this.file = new HdfFile(new File(h5Filename));
    }

    public void close() {
        if (file != null) {
            file.close();
            file = null;
        }
    }

    // ---------------- 基础信息 ----------------
    public Map<String, Object> readBaseInfo() {
        Group nastran = getGroup("/NASTRAN");
        return recursiveGroupAttrs(nastran);
    }

    private Map<String, Object> recursiveGroupAttrs(Group group) {
        Map<String, Object> info = new HashMap<>();

        // 把 Attribute 对象展开为真实值（不做强制 decode，若是 byte[] 则转成 String）
        Map<String, Object> attrs = new HashMap<>();
        for (Map.Entry<String, Attribute> e : group.getAttributes().entrySet()) {
            Object val = e.getValue().getData();
            if (val instanceof byte[]) {
                // 把 byte[] 当作字符串来读（trim），如果你不希望 decode，可以把下面一行注掉或改成 attrs.put(e.getKey(), val);
                val = new String((byte[]) val, StandardCharsets.UTF_8).trim();
            }
            attrs.put(e.getKey(), val);
        }
        info.put("_attrs", attrs);

        // 递归子 group
        for (Map.Entry<String, Node> childEntry : group.getChildren().entrySet()) {
            String key = childEntry.getKey();
            Node child = childEntry.getValue();
            if (child instanceof Group) {
                info.put(key, recursiveGroupAttrs((Group) child));
            }
        }
        return info;
    }

    // ---------------- 结果域 ----------------
    public List<Map<String, Object>> readResultDomains() {
        Dataset ds = getDataset("/NASTRAN/RESULT/DOMAINS");
        // decodeBytes = true 可以把 byte[] -> String（常见），你可以改为 false 不 decode
        return getRows(ds, true);
    }

    // ---------------- 读取单个 dataset -> 返回 NastranData.NastranDataset ----------------
    @SuppressWarnings("unchecked")
    public NastranData.NastranDataset readDataset(NastranDatasetConfig config, Map<Object, Object> domainSubcaseMap) {
        Dataset ds = getDataset(config.getPath());
        // 如果你不想自动把 byte[] 解码成 String，把第二个参数设为 false
        List<Map<String, Object>> rows = getRows(ds, true);

        Map<Integer, List<NastranData.NastranDataEntry>> subcases = new HashMap<>();

        for (Map<String, Object> row : rows) {
            // 注意：row.get("DOMAIN_ID") 可能返回 Integer/Long/Double/byte[] 等
            Object rawDomainId = row.get("DOMAIN_ID");
            if (rawDomainId == null) continue;
            Object normDomainId = normalizeKey(rawDomainId); // Long or String

            if (!domainSubcaseMap.containsKey(normDomainId)) continue;
            Object rawSubcase = domainSubcaseMap.get(normDomainId);
            int subcaseId = toIntSubcase(rawSubcase);

            Object entityObj = row.get((String) config.getFieldMapping().get("element"));
            String entityStr = objectToString(entityObj, true); // decode bytes to string

            List<Object> values = new ArrayList<>();
            for (String f : (List<String>) config.getFieldMapping().get("values")) {
                Object v = row.get(f);
                values.add(decodeIfBytes(v, true)); // decode bytes -> string if desired
            }

            Integer entityId = Integer.parseInt(entityStr);
            NastranData.NastranDataEntry entry = new NastranData.NastranDataEntry(entityId, values);
            subcases.computeIfAbsent(subcaseId, k -> new ArrayList<>()).add(entry);
        }

        return new NastranData.NastranDataset(config.getResultName(), config.getHeaders(), subcases);
    }

    // ---------------- 将 jHDF 的 dataset data 转成 行 列表 ----------------
    @SuppressWarnings("unchecked")
    private List<Map<String, Object>> getRows(Dataset ds, boolean decodeBytes) {
        Object data = ds.getData();
        if (data == null) return Collections.emptyList();

        // 1) 如果 jHDF 已经返回 List<Map<>>, 直接返回
        if (data instanceof List<?>) {
            List<?> list = (List<?>) data;
            if (list.isEmpty()) return Collections.emptyList();
            if (list.get(0) instanceof Map<?, ?>) {
                return (List<Map<String, Object>>) list;
            }
        }

        // 2) 如果是单个 Map（行式或列式）
        if (data instanceof Map<?, ?>) {
            Map<String, Object> colMap = (Map<String, Object>) data;

            // 若 Map 的每个 value 是数组/Collection -> 列式，需要转成 rows
            int length = determineLength(colMap);
            if (length <= 0) {
                // treat as single-row map
                Map<String, Object> single = new HashMap<>();
                for (Map.Entry<String, Object> e : colMap.entrySet()) {
                    single.put(e.getKey(), decodeIfBytes(e.getValue(), decodeBytes));
                }
                return List.of(single);
            }

            List<Map<String, Object>> rows = new ArrayList<>(length);
            for (int i = 0; i < length; i++) {
                Map<String, Object> row = new HashMap<>();
                for (Map.Entry<String, Object> e : colMap.entrySet()) {
                    Object col = e.getValue();
                    Object v = extractFromColumn(col, i);
                    v = decodeIfBytes(v, decodeBytes);
                    row.put(e.getKey(), v);
                }
                rows.add(row);
            }
            return rows;
        }

        // 3) 如果 data 是数组（可能是 Map[]、Object[]）
        if (data.getClass().isArray()) {
            int len = Array.getLength(data);
            List<Map<String, Object>> rows = new ArrayList<>(len);
            for (int i = 0; i < len; i++) {
                Object el = Array.get(data, i);
                if (el instanceof Map<?, ?>) {
                    rows.add((Map<String, Object>) el);
                } else {
                    rows.add(Map.of("value", el));
                }
            }
            return rows;
        }

        // 其它不支持类型
        throw new RuntimeException("Unexpected dataset type: " + data.getClass());
    }

    // determine length for column maps: find first array/collection and return its length
    private int determineLength(Map<String, Object> colMap) {
        for (Object v : colMap.values()) {
            if (v == null) continue;
            if (v instanceof Collection<?>) return ((Collection<?>) v).size();
            if (v.getClass().isArray()) return Array.getLength(v);
        }
        return -1;
    }

    // 从列结构取 index 项（支持 primitive array / Object[] / Collection）
    private Object extractFromColumn(Object column, int index) {
        if (column == null) return null;
        if (column instanceof Collection<?>) {
            Iterator<?> it = ((Collection<?>) column).iterator();
            for (int i = 0; i <= index; i++) {
                if (!it.hasNext()) return null;
                Object val = it.next();
                if (i == index) return val;
            }
            return null;
        }
        if (column.getClass().isArray()) {
            // handles primitive arrays as well
            return Array.get(column, index);
        }
        // scalar -> same value for each row (rare)
        return column;
    }

    // decode byte[] -> String if requested
    private Object decodeIfBytes(Object v, boolean decode) {
        if (!decode) return v;
        if (v instanceof byte[]) return new String((byte[]) v, StandardCharsets.UTF_8).trim();
        return v;
    }

    // normalize keys for consistent lookup: numbers -> Long, byte[] -> String
    private Object normalizeKey(Object key) {
        if (key == null) return null;
        if (key instanceof Number) return ((Number) key).longValue();
        if (key instanceof byte[]) return new String((byte[]) key, StandardCharsets.UTF_8).trim();
        return key.toString(); // fallback: use string form
    }

    // convert subcase object to int
    private int toIntSubcase(Object subcaseObj) {
        if (subcaseObj instanceof Number) return ((Number) subcaseObj).intValue();
        if (subcaseObj instanceof byte[]) {
            String s = new String((byte[]) subcaseObj, StandardCharsets.UTF_8).trim();
            return Integer.parseInt(s);
        }
        return Integer.parseInt(subcaseObj.toString());
    }

    // helper to safely stringify entity (decode bytes optional)
    private String objectToString(Object o, boolean decodeBytes) {
        Object v = decodeIfBytes(o, decodeBytes);
        return v == null ? null : v.toString();
    }

    // ---------------- loadResults ----------------
    public NastranData loadResults() {
        Map<String, Object> baseInfo = readBaseInfo();
        // read domains (decoded)
        List<Map<String, Object>> domains = readResultDomains();

        // build domainId -> subcase map with normalized keys (Long)
        Map<Object, Object> domainSubcaseMap = new HashMap<>();
        for (Map<String, Object> d : domains) {
            Object id = normalizeKey(d.get("ID"));
            Object subcase = d.get("SUBCASE"); // keep raw, toIntSubcase will handle
            domainSubcaseMap.put(id, subcase);
        }

        Map<String, NastranData.NastranDataset> results = new HashMap<>();
        for (NastranDatasetConfig ds : NastranDatasetConfig.values()) {
            try {
                NastranData.NastranDataset dataset = readDataset(ds, domainSubcaseMap);
                results.put(ds.getResultName(), dataset);
            } catch (RuntimeException ignored) {
                // dataset not present or parsing error -> skip
            }
        }

        Map<String, Object> attrs = (Map<String, Object>) baseInfo.get("_attrs");
        String version = "unknown";
        if (attrs != null && attrs.containsKey("VERSION")) version = attrs.get("VERSION").toString();

        this.data = new NastranData(version, results);
        return this.data;
    }

    // ---------------- Node helpers ----------------
    private Group getGroup(String path) {
        if (file == null) throw new RuntimeException("HDF5 file not opened.");
        Node node = file.getByPath(path);
        if (!(node instanceof Group)) throw new RuntimeException("Group " + path + " not found.");
        return (Group) node;
    }

    private Dataset getDataset(String path) {
        if (file == null) throw new RuntimeException("HDF5 file not opened.");
        Node node = file.getByPath(path);
        if (!(node instanceof Dataset)) throw new RuntimeException("Dataset " + path + " not found.");
        return (Dataset) node;
    }

    // convenience static loader
    public static NastranData loadHdf5AsOp2Result(String filepath) throws IOException {
        NastranHdf5Reader reader = new NastranHdf5Reader();
        reader.open(filepath);
        NastranData data = reader.loadResults();
        reader.close();
        return data;
    }

    public static IsfdMeshOp2Object parseAndconvertToOp2Object(String filePath) {
        NastranData nastranData;
        try{
            nastranData = loadHdf5AsOp2Result(filePath);
            return convertH5toOp2(nastranData);
        } catch (Exception e){
            throw new BaseException("H5文件转换失败，message:" + e.getMessage());
        }
    }

    /**
     * 将解析好的 NastranData 转换为 IsfdMeshOp2Object
     */
    public static IsfdMeshOp2Object convertH5toOp2(NastranData nastranData) {
        IsfdMeshOp2Object target = new IsfdMeshOp2Object();
        List<IsfdMeshOp2Result> resultList = new ArrayList<>();

        if (nastranData == null || nastranData.getResults() == null) {
            target.setResultList(resultList);
            return target;
        }

        for (NastranData.NastranDataset dataset : nastranData.getResults().values()) {
            IsfdMeshOp2Result result = new IsfdMeshOp2Result();
            result.setResultName(dataset.getName());

            List<IsfdMeshOp2SubcaseBo> subcaseList = new ArrayList<>();
            for (Map.Entry<Integer, List<NastranData.NastranDataEntry>> entry : dataset.getOp2Subcases().entrySet()) {
                Integer subcaseId = entry.getKey();
                List<NastranData.NastranDataEntry> entries = entry.getValue();

                IsfdMeshOp2SubcaseBo subcase = new IsfdMeshOp2SubcaseBo();
                subcase.setSubcaseId(subcaseId);
                subcase.setHeaders(dataset.getHeaders());

                Map<Integer, float[]> dataMap = new HashMap<>();
                for (NastranData.NastranDataEntry dataEntry : entries) {
                    Integer entityId = dataEntry.getEntity();
                    float[] values = new float[dataEntry.getValues().size()];
                    for (int i = 0; i < dataEntry.getValues().size(); i++) {
                        Object val = dataEntry.getValues().get(i);
                        try {
                            values[i] = Float.parseFloat(val.toString());
                        } catch (Exception ex) {
                            throw new RuntimeException("值转换为 float 失败, entity="
                                + dataEntry.getEntity() + ", value=" + val, ex);
                        }
                    }
                    dataMap.put(entityId, values);
                }

                subcase.setData(dataMap);
                subcase.setElementQuantity(dataMap.size());
                subcase.setColumnQuantity(dataset.getHeaders() != null ? dataset.getHeaders().size() : 0);

                subcaseList.add(subcase);
            }

            result.setSubcaseList(subcaseList);
            resultList.add(result);
        }

        target.setResultList(resultList);
        return target;
    }

    public static void main(String[] args) {
        String FILE_FOLDER = "C:\\Users\\lijpt\\OneDrive\\Documents\\COMAC\\ISFD\\结果库\\改造\\";
        String FILE_NAME = "aircraft.h5";
        String filepath = FILE_FOLDER + FILE_NAME;


        IsfdMeshOp2Object op2Object = parseAndconvertToOp2Object(filepath);

//        NastranData data = new NastranData();
//        try {
//            data = loadHdf5AsOp2Result(filepath);
//        } catch (Exception e) {
//            throw new BaseException(e.getMessage());
//        }
        System.out.println("success");
    }
}
